package b649a01.runners;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.Vector;

import b649a01.tomasulo.*;

/***
 * 
 * An interpreter for parsing the TRITE code based on simple 5-stage pipelined
 * architecture.
 * 
 * @author Chao Sun
 * @version 1.0
 * 
 */
public class sim_t {
	private String cycleFilePath, dumpFilePath, codeFilePath, outPutFilePath;
	private Hashtable<String, Integer> cycle_rsSizeTable, branchTable;
	private byte[] dumpArray;
	private int insNum;
	private String[] insArray;
	private int ldNum, fldNum, sNum, fsNum, addNum, mulNum, divNum, faddNum,
			fmulNum, fdivNum;
	private rob_value_pair[] R, F;
	private LinkedList<rs_buf> loadBuf, storeBuf, addRS, mulRS, divRS, faddRS,
			fmulRS, fdivRS;
	private int loadSize, storeSize, addSize, mulSize, divSize, faddSize,
			fmulSize, fdivSize;
	private LinkedList<rob> ROB;
	private Vector<rob_value_pair> CDB;
	private Vector<to_exe> inExe;
	private int pipelineIndex, branchIndex, reservedIndex;
	private boolean branchFlag, stallFlag, wrongPredict;
	private int cycles, stalls, actualIns;

	/**
	 * Constructor.
	 * 
	 * @param args
	 *          the command line string array
	 * @throws IOException
	 * @throws NumberFormatException
	 */
	public sim_t(String args[]) throws NumberFormatException, IOException {
		cycleFilePath = args[0];
		dumpFilePath = args[1];
		codeFilePath = args[2];
		outPutFilePath = args[3];

		String line;
		// read the cycle number file
		cycle_rsSizeTable = new Hashtable<String, Integer>();
		BufferedReader cycle = new BufferedReader(new FileReader(cycleFilePath));
		while ((line = cycle.readLine()) != null) {
			StringTokenizer ct = new StringTokenizer(line);
			cycle_rsSizeTable.put(ct.nextToken(), Integer.parseInt(ct.nextToken()));
		}
		// read sizes of reservation stations, load/store buffers
		loadSize = cycle_rsSizeTable.get("loads");
		storeSize = cycle_rsSizeTable.get("stores");
		addSize = cycle_rsSizeTable.get("intadds");
		mulSize = cycle_rsSizeTable.get("intmuls");
		divSize = cycle_rsSizeTable.get("intdivs");
		faddSize = cycle_rsSizeTable.get("fpadds");
		fmulSize = cycle_rsSizeTable.get("fpmuls");
		fdivSize = cycle_rsSizeTable.get("fpdivs");
		// initiate reservation stations, load/store buffers, ROB, CDB etc.
		loadBuf = new LinkedList<rs_buf>();
		storeBuf = new LinkedList<rs_buf>();
		addRS = new LinkedList<rs_buf>();
		mulRS = new LinkedList<rs_buf>();
		divRS = new LinkedList<rs_buf>();
		faddRS = new LinkedList<rs_buf>();
		fmulRS = new LinkedList<rs_buf>();
		fdivRS = new LinkedList<rs_buf>();
		ROB = new LinkedList<rob>();
		CDB = new Vector<rob_value_pair>();
		inExe = new Vector<to_exe>();
		// read computing unit quantities
		if (cycle_rsSizeTable.containsKey("loadu")) {
			ldNum = cycle_rsSizeTable.get("loadu");
			fldNum = cycle_rsSizeTable.get("floadu");
			sNum = cycle_rsSizeTable.get("storeu");
			fsNum = cycle_rsSizeTable.get("fstoreu");
			addNum = cycle_rsSizeTable.get("intaddu");
			mulNum = cycle_rsSizeTable.get("intmulu");
			divNum = cycle_rsSizeTable.get("intdivu");
			faddNum = cycle_rsSizeTable.get("fpaddu");
			fmulNum = cycle_rsSizeTable.get("fpmulu");
			fdivNum = cycle_rsSizeTable.get("fpdivu");
		} else {
			ldNum = fldNum = sNum = fsNum = addNum = mulNum = divNum = faddNum = fmulNum = fdivNum = 1;
		}
		// create memory dump area
		dumpArray = new byte[1048576];
		BufferedReader dump = new BufferedReader(new FileReader(dumpFilePath));
		for (int i = 0; i < dumpArray.length; i++) {
			dumpArray[i] = (byte) dump.read();
		}
		// build instruction array
		BufferedReader in = new BufferedReader(new FileReader(codeFilePath));
		LinkedList<String> lineList = new LinkedList<String>();
		while ((line = in.readLine()) != null) {
			lineList.add(line);
		}
		insNum = lineList.size();
		insArray = new String[insNum];
		for (int i = 0; i < insNum; i++) {
			StringTokenizer lineTokenizer = new StringTokenizer(lineList.poll(), ";");
			String instruction = lineTokenizer.nextToken();
			insArray[i] = instruction;
		}

		R = new rob_value_pair[16];
		F = new rob_value_pair[16];
		for (int i = 0; i < 16; i++) {
			R[i] = new rob_value_pair();
			F[i] = new rob_value_pair();
		}
		pipelineIndex = cycles = stalls = actualIns = 0;
		branchIndex = reservedIndex = -1;
		branchFlag = stallFlag = wrongPredict = false;
		branchTable = new Hashtable<String, Integer>();
	}

	/**
	 * Run.
	 * 
	 * @throws IOException
	 */
	private void run() throws IOException {
		int i = 0;
		while (i < insNum || !ROB.isEmpty()) {
			cycles++;
			// check ROB and commit
			boolean flag = false;
			while (!ROB.isEmpty()) {
				rob r = ROB.getFirst();
				if (r.getStep() == 4) {
					r.commit();
					ROB.removeFirst();
					actualIns++;
					flag = true;
				} else {
					if (flag == false) { // stall
						stalls++;
						stallFlag = true;
					}
					break;
				}
			}
			// write result
			while (!CDB.isEmpty()) {
				rob_value_pair rvp = CDB.remove(0);
				for (int j = 0; j < ROB.size(); j++) {
					rob r = ROB.get(j);
					if (r.getEntry() == rvp.getROBEntry()) {
						r.setValue(rvp.getValue());
						r.setStep(4);
					}
				}
			}
			// select instructions and execute
			select();
			if (inExe.isEmpty() && stallFlag == false) { // stall
				stalls++;
				stallFlag = true;
			}
			for (int j = 0; j < inExe.size();) {
				to_exe ins = inExe.get(j);
				if (ins.execute()) {
					rob_value_pair data = new rob_value_pair();
					data.setROBEntry(ins.getROBEntry());
					data.setValue(ins.getValue());
					CDB.add(data);
					inExe.remove(j);
					updateROB(ins.getROBEntry(), 3);
					if (wrongPredict) { // flush all following instructions
						i = reservedIndex;
						flushIns(ins.getROBEntry());
						break;
					}
				} else {
					updateROB(ins.getROBEntry(), 2);
					j++;
				}
			}
			if (wrongPredict) {
				wrongPredict = false;
				continue;
			}
			// issue an instruction
			if (i < insNum) {
				int issueResult = issueIns(i);
				if (issueResult >= 0) {
					i++;
					if (issueResult == 1) { // empty instruction
					}
					if (branchFlag) { // predict taking a branch
						reservedIndex = i;
						i = branchIndex;
						branchFlag = false;
					}
				} else if (stallFlag == false) { // stall
					stalls++;
					stallFlag = true;
				}
			}

			if (stallFlag == true) {
				stallFlag = false;
			}
		}

		printDump(); // printout dump area including computing results
		// write simulation results into file
		double CPI = (double) cycles / actualIns;
		FileWriter writer = new FileWriter(outPutFilePath);
		writer.write("Tomasulo Algorithm Simulation Results \n\n"
				+ "Total Cycles: " + cycles + " \nTotal Stalls: " + stalls
				+ " \nActual Instructions: " + actualIns + " \nAverage CPI: " + CPI
				+ "\n");
		writer.flush();
	}

	/**
	 * Print out the computing results.
	 */
	private void printDump() {
		int index = 1;
		
		/*for (int j = 0; j < 300; j++) { // integer
			if (j % 10 == 0) {
				System.out.println("");
			}
			if (j % 100 == 0) {
				System.out.println("\n");
			}
			ByteBuffer buffer = ByteBuffer.allocate(4);
			buffer.put(dumpArray, j * 4, 4);
			System.out.print(buffer.getInt(0) + "\t");
		}*/

		for (int j = 0; j < 300; j++) { // floating point
			if (j % 10 == 0) {
				System.out.println("");
			}
			if (j % 100 == 0) {
				System.out.println("\nMatrix" + index++ + ":\n");
			}
			ByteBuffer buffer = ByteBuffer.allocate(8);
			buffer.put(dumpArray, j * 8, 8);
			System.out.print(buffer.getDouble(0) + "\t");
		}
		System.out.println("\n");
	}

	/**
	 * Issue an instruction.
	 * 
	 * @param i
	 *          the instruction index
	 * @return an integer indicating if this is an empty instruction
	 */
	private int issueIns(int i) {
		String instruction = insArray[i];
		// break the instruction into operation and operand
		StringTokenizer insTokenizer = new StringTokenizer(instruction);
		String first = null;
		try {
			first = insTokenizer.nextToken();
		} catch (NoSuchElementException e) {
			return 1; // empty
		}
		// assign operation and operand
		String operation, operand = null;
		StringTokenizer opTokenizer = null;
		if (first.endsWith(":")) { // this is a branch
			branchTable.put(first.substring(0, first.length() - 1), i);
			operation = insTokenizer.nextToken();
		} else { // other instruction
			operation = first;
		}
		if (operation.equals("NOOP")) {
			actualIns++;
			return 0;
		}
		operand = insTokenizer.nextToken();
		opTokenizer = new StringTokenizer(operand, ",");
		// try creating ROB and RS elements
		boolean slotFlag = true;
		LinkedList<rs_buf> list = null;
		if (operation.equals("L") || operation.equals("LI")
				|| operation.equals("L.D")) {
			slotFlag = (loadBuf.size() < loadSize);
			list = loadBuf;
		} else if (operation.equals("S") || operation.equals("S.D")) {
			slotFlag = (storeBuf.size() < storeSize);
			list = storeBuf;
		} else if (operation.equals("ADD") || operation.equals("SUB")
				|| operation.equals("BEQ") || operation.equals("BNEQ")
				|| operation.equals("BGTZ") || operation.equals("BLTZ")
				|| operation.equals("MOVE")) {
			slotFlag = (addRS.size() < addSize);
			list = addRS;
		} else if (operation.equals("MUL")) {
			slotFlag = (mulRS.size() < mulSize);
			list = mulRS;
		} else if (operation.equals("DIV")) {
			slotFlag = (divRS.size() < divSize);
			list = divRS;
		} else if (operation.equals("ADD.D") || operation.equals("SUB.D")
				|| operation.equals("BEQ.D") || operation.equals("BNEQ.D")
				|| operation.equals("BGTZ.D") || operation.equals("BLTZ.D")
				|| operation.equals("MOVE.D")) {
			slotFlag = (faddRS.size() < faddSize);
			list = faddRS;
		} else if (operation.equals("MUL.D")) {
			slotFlag = (fmulRS.size() < fmulSize);
			list = fmulRS;
		} else if (operation.equals("DIV.D")) {
			slotFlag = (fdivRS.size() < fdivSize);
			list = fdivRS;
		} else {
			System.err.println("Unknown operator! Exiting ...");
			System.exit(0);
		}
		if (slotFlag) {
			rs_buf rs = new rs_buf(this, pipelineIndex, operation, opTokenizer);
			list.add(rs);
			rob r = new rob(this, pipelineIndex, operation, rs.getTarget());
			ROB.add(r);
			pipelineIndex++;
		} else {
			stallFlag = true;
			stalls++;
			return -1; // no open slot
		}
		// issued one
		return 0;
	}

	/**
	 * Select instructions for executing from each reservation station or buffer.
	 */
	private void select() {
		int ldN = 0, fldN = 0, sN = 0, fsN = 0;
		int addN = 0, mulN = 0, divN = 0, faddN = 0, fmulN = 0, fdivN = 0;
		int i = 0;
		boolean flag1, flag2;

		for (; i < loadBuf.size();) {
			rs_buf buffer = loadBuf.get(i);
			if (checkVj(buffer)) { // vj in place
				if ((!buffer.getOperator().equals("L.D") && ldN < ldNum)
						|| (buffer.getOperator().equals("L.D") && fldN < fldNum)) {
					String op = buffer.getOperator();
					to_exe sel = new to_exe(this, buffer.getEntry(), op, buffer.getVj(),
							null, cycle_rsSizeTable.get(op));
					inExe.add(sel);
					loadBuf.remove(i);
					if (buffer.getOperator().equals("L.D")) {
						fldN++;
					} else {
						ldN++;
					}
				} else {
					i++;
				}
			} else if (buffer.getQj() == -1 && checkVk(buffer) && buffer.setOffset()) {
				if (buffer.getOperator().equals("L")) {
					buffer.loadMem(4); // try read memory
				} else {
					buffer.loadMem(8);
				}
				if (!checkVj(buffer)) { // read nothing
					i++;
				}
			} else { // no vj, no vk, no offset
				i++;
			}
		}

		for (i = 0; i < storeBuf.size();) {
			rs_buf buffer = storeBuf.get(i);
			flag1 = checkVj(buffer);
			flag2 = checkVk(buffer);
			if (flag1 && flag2) {
				if (buffer.getOffset() < 0) {
					buffer.setOffset();
				}
				for (int j = 0; j < ROB.size(); j++) {
					rob r = ROB.get(j);
					if (r.getEntry() == buffer.getEntry()) {
						r.setDest(Integer.toString(buffer.getOffset()));
						break;
					}
				}
				if ((buffer.getOperator().equals("S") && sN < sNum)
						|| (buffer.getOperator().equals("S.D") && fsN < fsNum)) {
					String op = buffer.getOperator();
					to_exe sel = new to_exe(this, buffer.getEntry(), op, buffer.getVj(),
							buffer.getVk(), cycle_rsSizeTable.get(op));
					inExe.add(sel);
					storeBuf.remove(i);
					if (buffer.getOperator().equals("S")) {
						sN++;
					} else {
						fsN++;
					}
				} else {
					i++;
				}
			} else {
				i++;
			}
		}

		for (i = 0; i < addRS.size();) {
			rs_buf rs = addRS.get(i);
			String op = rs.getOperator();
			to_exe sel = null;
			if ((op.equals("BGTZ") || op.equals("BLTZ") || op.equals("MOVE"))) {
				if (!checkVj(rs)) {
					i++;
					continue;
				}
				if (addN < addNum) {
					sel = new to_exe(this, rs.getEntry(), op, rs.getVj(), null,
							cycle_rsSizeTable.get(op));
					inExe.add(sel);
					addRS.remove(i);
					addN++;
				} else {
					i++;
				}
			} else {
				flag1 = checkVk(rs);
				flag2 = checkVj(rs);
				if (!flag1 || !flag2) {
					i++;
					continue;
				}
				if (addN < addNum) {
					sel = new to_exe(this, rs.getEntry(), op, rs.getVj(), rs.getVk(),
							cycle_rsSizeTable.get(op));
					inExe.add(sel);
					addRS.remove(i);
					addN++;
				} else {
					i++;
				}
			}
		}

		for (i = 0; i < mulRS.size();) {
			rs_buf rs = mulRS.get(i);
			flag1 = checkVk(rs);
			flag2 = checkVj(rs);
			if (!flag1 || !flag2) {
				i++;
				continue;
			}
			if (mulN < mulNum) {
				String op = rs.getOperator();
				to_exe sel = new to_exe(this, rs.getEntry(), op, rs.getVj(),
						rs.getVk(), cycle_rsSizeTable.get(op));
				inExe.add(sel);
				mulRS.remove(i);
				mulN++;
			} else {
				i++;
			}
		}

		for (i = 0; i < divRS.size();) {
			rs_buf rs = divRS.get(i);
			flag1 = checkVk(rs);
			flag2 = checkVj(rs);
			if (!flag1 || !flag2) {
				i++;
				continue;
			}
			if (divN < divNum) {
				String op = rs.getOperator();
				to_exe sel = new to_exe(this, rs.getEntry(), op, rs.getVj(),
						rs.getVk(), cycle_rsSizeTable.get(op));
				inExe.add(sel);
				divRS.remove(i);
				divN++;
			} else {
				i++;
			}
		}

		for (i = 0; i < faddRS.size();) {
			rs_buf rs = faddRS.get(i);
			String op = rs.getOperator();
			to_exe sel = null;
			if ((op.equals("BGTZ.D") || op.equals("BLTZ.D") || op.equals("MOVE.D"))) {
				if (!checkVj(rs)) {
					i++;
					continue;
				}
				if (faddN < faddNum) {
					sel = new to_exe(this, rs.getEntry(), op, rs.getVj(), null,
							cycle_rsSizeTable.get(op));
					inExe.add(sel);
					faddRS.remove(i);
					faddN++;
				} else {
					i++;
				}
			} else {
				flag1 = checkVk(rs);
				flag2 = checkVj(rs);
				if (!flag1 || !flag2) {
					i++;
					continue;
				}
				if (faddN < faddNum) {
					sel = new to_exe(this, rs.getEntry(), op, rs.getVj(), rs.getVk(),
							cycle_rsSizeTable.get(op));
					inExe.add(sel);
					faddRS.remove(i);
					faddN++;
				} else {
					i++;
				}
			}
		}

		for (i = 0; i < fmulRS.size();) {
			rs_buf rs = fmulRS.get(i);
			flag1 = checkVk(rs);
			flag2 = checkVj(rs);
			if (!flag1 || !flag2) {
				i++;
				continue;
			}
			if (fmulN < fmulNum) {
				String op = rs.getOperator();
				to_exe sel = new to_exe(this, rs.getEntry(), op, rs.getVj(),
						rs.getVk(), cycle_rsSizeTable.get(op));
				inExe.add(sel);
				fmulRS.remove(i);
				fmulN++;
			} else {
				i++;
			}
		}

		for (i = 0; i < fdivRS.size(); i++) {
			rs_buf rs = fdivRS.get(i);
			flag1 = checkVk(rs);
			flag2 = checkVj(rs);
			if (!flag1 || !flag2) {
				i++;
				continue;
			}
			if (fdivN < fdivNum) {
				String op = rs.getOperator();
				to_exe sel = new to_exe(this, rs.getEntry(), op, rs.getVj(),
						rs.getVk(), cycle_rsSizeTable.get(op));
				inExe.add(sel);
				fdivRS.remove(i);
				fdivN++;
			} else {
				i++;
			}
		}
	}

	/**
	 * Try to find a branch by name in the instruction array.
	 * 
	 * @param br
	 *          the label of the branch to be searched for
	 * @return an integer value indicating: (n) the index of this branch in the
	 *         instruction array; (-1) no such branch
	 */
	public int searchBranch(String br) {
		for (int i = 0; i < insNum; i++) {
			String instruction = insArray[i];
			StringTokenizer insTokenizer = new StringTokenizer(instruction);
			String first = insTokenizer.nextToken();
			if (first.endsWith(":")) {
				String bra = first.substring(0, first.length() - 1);
				if (bra.equals(br)) {
					branchTable.put(bra, i);
					return i;
				}
			}
		}
		return -1;
	}

	private void updateROB(int entry, int step) {
		for (int i = 0; i < ROB.size(); i++) {
			rob r = ROB.get(i);
			if (r.getEntry() == entry) {
				r.setStep(step);
				return;
			}
		}
	}

	public Double monitorROB(int entry) {
		for (int i = 0; i < ROB.size(); i++) {
			rob r = ROB.get(i);
			if (r.getEntry() == entry) {
				if (r.getStep() == 4) {
					Double v = r.getValue();
					return v;
				} else if (r.getStep() == 3) {
					for (int j = 0; j < CDB.size(); j++) {
						rob_value_pair c = CDB.get(j);
						if (c.getROBEntry() == entry) {
							Double v = c.getValue();
							return v;
						}
					}
				}
			}
		}
		return null;
	}

	private boolean checkVj(rs_buf buffer) {
		if (buffer.getVj() != null) {
			return true;
		}
		Double o = monitorROB(buffer.getQj());
		if (o == null) {
			return false;
		} else {
			buffer.setVj(o);
			return true;
		}
	}

	private boolean checkVk(rs_buf buffer) {
		if (buffer.getVk() != null) {
			return true;
		}
		Double o = monitorROB(buffer.getQk());
		if (o == null) {
			return false;
		} else {
			buffer.setVk(o);
			return true;
		}
	}

	private void flushIns(int index) {
		for (int i = 0; i < 16; i++) { // R, F
			if (R[i].getROBEntry() > index) {
				R[i].setROBEntry(-1);
			}
			if (F[i].getROBEntry() > index) {
				F[i].setROBEntry(-1);
			}
		}
		for (int i = 0; i < loadBuf.size();) {
			rs_buf r = loadBuf.get(i);
			if (r.getEntry() > index) {
				loadBuf.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < storeBuf.size();) {
			rs_buf r = storeBuf.get(i);
			if (r.getEntry() > index) {
				storeBuf.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < addRS.size();) {
			rs_buf r = addRS.get(i);
			if (r.getEntry() > index) {
				addRS.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < mulRS.size();) {
			rs_buf r = mulRS.get(i);
			if (r.getEntry() > index) {
				mulRS.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < divRS.size();) {
			rs_buf r = divRS.get(i);
			if (r.getEntry() > index) {
				divRS.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < faddRS.size();) {
			rs_buf r = faddRS.get(i);
			if (r.getEntry() > index) {
				faddRS.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < fmulRS.size();) {
			rs_buf r = fmulRS.get(i);
			if (r.getEntry() > index) {
				fmulRS.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < fdivRS.size();) {
			rs_buf r = fdivRS.get(i);
			if (r.getEntry() > index) {
				fdivRS.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < CDB.size();) {
			rob_value_pair r = CDB.get(i);
			if (r.getROBEntry() > index) {
				CDB.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < ROB.size();) {
			rob r = ROB.get(i);
			if (r.getEntry() > index) {
				ROB.remove(i);
			} else {
				i++;
			}
		}
		for (int i = 0; i < inExe.size();) {
			to_exe r = inExe.get(i);
			if (r.getROBEntry() > index) {
				inExe.remove(i);
			} else {
				i++;
			}
		}
	}

	public byte[] getDump() {
		return dumpArray;
	}

	public void writeDump(byte value, int offset) {
		dumpArray[offset] = value;
	}

	public Hashtable<String, Integer> getbranchTable() {
		return branchTable;
	}

	public rob_value_pair[] getR() {
		return R;
	}

	public void setR(int index, Double value, int entry) {
		if (entry != -2) {
			R[index].setROBEntry(entry);
		}
		R[index].setValue(value);
	}

	public rob_value_pair[] getF() {
		return F;
	}

	public void setF(int index, Double value, int entry) {
		if (entry != -2) {
			F[index].setROBEntry(entry);
		}
		F[index].setValue(value);
	}

	public LinkedList<rs_buf> getStoreBuf() {
		return storeBuf;
	}

	public Vector<to_exe> getExe() {
		return inExe;
	}

	public LinkedList<rob> getROB() {
		return ROB;
	}

	public Vector<rob_value_pair> getCDB() {
		return CDB;
	}

	public void setBranch(int index) {
		branchFlag = true;
		branchIndex = index;
	}

	public void wrongPredict() {
		wrongPredict = true;
	}

	/**
	 * main method for testing.
	 * 
	 * @param args
	 *          the command line string array
	 */
	public static void main(String args[]) {
		if (args.length != 4) {
			System.err.println("Args error! Usage: java sim_p "
					+ "<ARCH_PARAMS> <INIT_MEM> <TRITE> <OUTPUT>");
			System.exit(1);
		}

		try {
			sim_t simulator = new sim_t(args);
			simulator.run();
		} catch (FileNotFoundException e) {
			System.err.println("File not found! Exiting ...");
			e.printStackTrace();
		} catch (IOException e) {
			System.err.println("File reading error! Exiting ...");
			e.printStackTrace();
		}
	}

}